package pl.edu.agh.neurony.gui;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JSpinner;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import pl.edu.agh.neurony.exceptions.BadFileFormatException;
import pl.edu.agh.neurony.exceptions.BadNeuronInitiationException;
import pl.edu.agh.neurony.exceptions.NeuronNetException;
import pl.edu.agh.neurony.gui.customSwingComponents.EtchedBorderPanel;
import pl.edu.agh.neurony.logic.Layer;
import pl.edu.agh.neurony.logic.Net;
import pl.edu.agh.neurony.logic.Neuron;
import pl.edu.agh.neurony.logic.activationFunctions.ActivationFunc;

public class NetCreationWindow extends JPanel {

	private int INPUT_CHANGED = 0;
	private int OUTPUT_CHANGED = 1;
	private int HIDDEN_LAYERS_CHANGED = 2;
	private LearningComponent learningComponent = new LearningComponent(this);
	OverallNetStructurePanel overalNetStructurePanel = new OverallNetStructurePanel(this);
	LayersTabsPane layerStructurePanel = new LayersTabsPane();
	LinkedList<JTextField> dataVector;

	public NetCreationWindow() {
		super(new GridBagLayout());

		GridBagConstraints c = new GridBagConstraints();
		c.gridwidth = GridBagConstraints.REMAINDER;
		c.fill = GridBagConstraints.BOTH;
		c.weightx = 1;
		c.weighty = 1;
		c.insets = new Insets(5, 5, 5, 5);

		add(new EtchedBorderPanel(overalNetStructurePanel, "Net structure"), c);
		add(layerStructurePanel, c);
	}

	public int getOutputVectorSize() {
		return (Integer) overalNetStructurePanel.outputVectorSizeSpinner.getValue();
	}

	public int getInputVectorSize() {
		return (Integer) overalNetStructurePanel.inputVectorSizeSpinner.getValue();
	}

	public int getHiddenLayersSize() {
		return (Integer) overalNetStructurePanel.hiddenLayersSizeSpinner.getValue();
	}

	public void onSpinnersChange(int event) throws NeuronNetException {
		Net net = Net.getInstance();
		if (event == HIDDEN_LAYERS_CHANGED) {
			int previousSize = net.size() - 2;
			int newSize = getHiddenLayersSize();
			if (newSize < previousSize) {
				layerStructurePanel.remove(previousSize + 1);
				List<Neuron> removedNeurons = net.removeLayer(previousSize);
				NeuralGraph.getInstance(false).removeLayer(removedNeurons);
			} else if (newSize > previousSize) {
				layerStructurePanel.addTab("Layer " + newSize, new LayerTab(newSize));
			}
		}
		if (event == INPUT_CHANGED) {
			int previousSize = net.sizeOfLayer(Layer.INPUT_LAYER);
			int newSize = getInputVectorSize();
			if (previousSize < newSize) {
				layerStructurePanel.extendDataVector();
				Neuron vertex = net.addNeuron(Layer.INPUT_LAYER);
				NeuralGraph.getInstance(false).addVertex(vertex, Layer.INPUT_LAYER, false);
			} else if (previousSize > newSize) {
				layerStructurePanel.reduceDataVector();
				Neuron vertex = net.removeLastNeuronInLayer(Layer.INPUT_LAYER);
				NeuralGraph.getInstance(false).removeVertex(vertex);
			}
		}
		if (event == OUTPUT_CHANGED) {
			int previousSize = net.sizeOfLayer(Layer.OUTPUT_LAYER);
			int newSize = getOutputVectorSize();
			if (previousSize < newSize) {
				layerStructurePanel.extendOutputNumber();
				Neuron vertex = net.addNeuron(Layer.OUTPUT_LAYER);
				NeuralGraph.getInstance(false).addVertex(vertex, Layer.OUTPUT_LAYER, false);
			} else if (previousSize > newSize) {
				layerStructurePanel.reduceOutputNumber();
				Neuron vertex = net.removeLastNeuronInLayer(Layer.OUTPUT_LAYER);
				NeuralGraph.getInstance(false).removeVertex(vertex);
			}
		}
	}

	class OverallNetStructurePanel extends JPanel implements ActionListener {

		private JSpinner inputVectorSizeSpinner = new JSpinner(new SpinnerNumberModel(1, 1, Integer.MAX_VALUE, 1));
		private JSpinner outputVectorSizeSpinner = new JSpinner(new SpinnerNumberModel(1, 1, Integer.MAX_VALUE, 1));
		private JSpinner hiddenLayersSizeSpinner = new JSpinner(new SpinnerNumberModel(0, 0, Integer.MAX_VALUE, 1));
		private JFileChooser fileChooser = new JFileChooser();

		JCheckBox generateRandomWeightsCheckBox;

		public OverallNetStructurePanel(NetCreationWindow parent) {
			super(new BorderLayout());
			inputVectorSizeSpinner.addChangeListener(new SpinnersChangeListener(parent));
			outputVectorSizeSpinner.addChangeListener(new SpinnersChangeListener(parent));
			hiddenLayersSizeSpinner.addChangeListener(new SpinnersChangeListener(parent));

			JPanel inputVectorSizePanel = new JPanel(new GridLayout());
			inputVectorSizePanel.add(new JLabel("input vector size: "));
			inputVectorSizePanel.add(inputVectorSizeSpinner);
			JPanel outputVectorSizePanel = new JPanel(new GridLayout());
			outputVectorSizePanel.add(new JLabel("output vector size: "));
			outputVectorSizePanel.add(outputVectorSizeSpinner);
			JPanel hiddenLayersSizePanel = new JPanel(new GridLayout());
			hiddenLayersSizePanel.add(new JLabel("hidden layers: "));
			hiddenLayersSizePanel.add(hiddenLayersSizeSpinner);

			JPanel spinnersPanel = new JPanel(new GridBagLayout());
			GridBagConstraints c = new GridBagConstraints();
			c.gridwidth = GridBagConstraints.REMAINDER;

			spinnersPanel.add(inputVectorSizePanel, c);
			spinnersPanel.add(hiddenLayersSizePanel, c);
			spinnersPanel.add(outputVectorSizePanel, c);

			// spinnersPanel.add(fromWeightPanel, c);
			// spinnersPanel.add(toWeightPanel, c);

			generateRandomWeightsCheckBox = new JCheckBox("Generate random weights", false);
			spinnersPanel.add(generateRandomWeightsCheckBox, c);
			add(spinnersPanel, BorderLayout.NORTH);

			JPanel buttonPlaceHolder = new JPanel(new GridLayout(2, 2));

			JButton readNetFromFile = new JButton("read net from file");
			readNetFromFile.setActionCommand("readNet");
			readNetFromFile.addActionListener(this);
			buttonPlaceHolder.add(readNetFromFile);

			JButton stimulateNetButton = new JButton("stimulate net");
			stimulateNetButton.setActionCommand("stimulate");
			stimulateNetButton.addActionListener(this);
			buttonPlaceHolder.add(stimulateNetButton);

			JButton learnNetButton = new JButton("Learn net");
			learnNetButton.setActionCommand("learn");
			learnNetButton.addActionListener(this);
			buttonPlaceHolder.add(learnNetButton);

			JButton testNetButton = new JButton("Test net");
			testNetButton.setActionCommand("test");
			testNetButton.addActionListener(this);
			buttonPlaceHolder.add(testNetButton);

			add(buttonPlaceHolder, BorderLayout.SOUTH);
		}

		private boolean isInputValid() {
			try {
				for (JTextField field : dataVector)
					Double.valueOf(field.getText());
			} catch (Exception ex) {
				return false;
			}

			return true;
		}

		@Override
		public void actionPerformed(ActionEvent event) {
			if (event.getActionCommand().equals("stimulate")) {
				if (isInputValid()) {
					Net net = Net.getInstance();
					net.resetValues(true, true);
					if (generateRandomWeightsCheckBox.isSelected()) {
						generateRandomWeights();
					}
					for (int i = 0; i < dataVector.size(); i++)
						net.fillInputNeuronWithSignal(i, Double.valueOf(dataVector.get(i).getText()));
					for (Component comp : layerStructurePanel.getComponents()) {
						if (comp instanceof BiasLayerTab) {
							((BiasLayerTab) comp).stimulateLayer();
						}
					}
					if (net.isValid()) {
						List<Double> output;
						try {
							output = net.stimulate();
							// System.out.println(net.toString());
							JOptionPane.showMessageDialog(this, "The net returned following response: " + output,
									"Net response", JOptionPane.INFORMATION_MESSAGE);
						} catch (BadNeuronInitiationException e) {
							JOptionPane.showMessageDialog(this,
									"The net is is unproperly constructed. Some of neurons has "
											+ "not their activation function set", "Net not valid",
									JOptionPane.ERROR_MESSAGE);
						}
					} else
						JOptionPane.showMessageDialog(this, "The net is not valid. Please check if all of edges has "
								+ "their weights set and all vertices has their activations functions set",
								"Net not valid", JOptionPane.ERROR_MESSAGE);
				} else
					JOptionPane.showMessageDialog(this, "Inputs are incorrectly filled. They are either empty or "
							+ "not a number", "Incorrect input", JOptionPane.ERROR_MESSAGE);
			} else if (event.getActionCommand().equals("readNet")) {
				int retVal = fileChooser.showDialog(this, "choose file");

				if (retVal == JFileChooser.APPROVE_OPTION) {
					try {
						NetParser.readNetFromFile(fileChooser.getSelectedFile(), this);
					} catch (IOException e) {
						JOptionPane.showMessageDialog(this, "Error during parsing net structure file",
								"Net file not valid", JOptionPane.ERROR_MESSAGE);
					} catch (BadFileFormatException e) {
						JOptionPane.showMessageDialog(this, "Error during parsing net structure file. File has wrong "
								+ "format", "Net file not valid", JOptionPane.ERROR_MESSAGE);
					} catch (NeuronNetException e) {
						JOptionPane.showMessageDialog(this, "Error during creating net", "Net file not valid",
								JOptionPane.ERROR_MESSAGE);
					}
				}
			} else if (event.getActionCommand().equals("learn")) {
				if (generateRandomWeightsCheckBox.isSelected()) {
					generateRandomWeights();
				}

				if (isInputValid()) {
					learningComponent.learn();
				} else {
					JOptionPane.showMessageDialog(this, "The net is not valid. Please check if all of edges has "
							+ "their weights set and all vertices has their activations functions set and input "
							+ "signals filled", "Net not valid", JOptionPane.ERROR_MESSAGE);
					JOptionPane.showMessageDialog(this, "The net is not valid. Please check if all of edges has "
							+ "their weights set and all vertices has their activations functions set and input "
							+ "signals filled", "Net not valid", JOptionPane.ERROR_MESSAGE);
				}
			} else if (event.getActionCommand().equals("test")) {
				try {
					Net.getInstance().test();
				} catch (BadNeuronInitiationException e) {

					e.printStackTrace();
				}
			}
		}

		public void generateRandomWeights() {
			for (Component comp : layerStructurePanel.getComponents()) {
				if (comp instanceof BiasLayerTab) {
					((BiasLayerTab) comp).generateRandomWeights();
				}
			}
			try {
				NeuralGraph.getInstance(false).repaint();
			} catch (NeuronNetException e) {
				e.printStackTrace();
			}
		}

		void updateSpinnersAndInputSignalData(String[] inputs, List<List<String[]>> hiddens,
				List<List<String[]>> kohonens, List<String[]> outputs) {
			Net net = Net.getInstance();
			ArrayList<List<String[]>> allHidden = new ArrayList<List<String[]>>();
			allHidden.addAll(hiddens);
			allHidden.addAll(kohonens);
			hiddenLayersSizeSpinner.setValue(allHidden.size());
			for (int i = 2; i < layerStructurePanel.getTabCount(); i++)
				layerStructurePanel.remove(i);
			for (int i = 0; i < allHidden.size(); i++) {
				ArrayList<Double> values = new ArrayList<Double>();
				for (String[] list : allHidden.get(i)) {
					values.add(Double.valueOf(list[list.length - 1]));
				}
				layerStructurePanel.addTab("Layer " + (i + 1), new LayerTab(i + 1, allHidden.get(i).size(), values, true));
			}
			int size = dataVector.size();

			inputVectorSizeSpinner.setValue(inputs.length);
			for (int i = 0; i < inputs.length - size; i++) {
				layerStructurePanel.extendDataVector();
			}
			for (int i = 0; i < size - inputs.length; i++) {
				layerStructurePanel.reduceDataVector();
			}
			for (int i = 0; i < dataVector.size(); i++)
				dataVector.get(i).setText(inputs[i]);

			for (int i = 0; i < outputs.size() - getOutputVectorSize(); i++) {
				layerStructurePanel.extendOutputNumber();
			}
			for (int i = 0; i < getOutputVectorSize() - outputs.size(); i++) {
				layerStructurePanel.reduceOutputNumber();
			}

			String[] tab = new String[outputs.size()];
			for (int i = 0; i < tab.length; i++) {
				tab[i] = outputs.get(i)[outputs.get(i).length - 1];
			}
			BiasLayerTab t = (BiasLayerTab) layerStructurePanel.getComponent(1);
			t.setBiasVector(tab);

			outputVectorSizeSpinner.setValue(outputs.size());
		}

		private class SpinnersChangeListener implements ChangeListener {
			NetCreationWindow parent;

			public SpinnersChangeListener(NetCreationWindow parent) {
				this.parent = parent;
			}

			@Override
			public void stateChanged(ChangeEvent e) {
				try {
					if (e.getSource() == inputVectorSizeSpinner)
						parent.onSpinnersChange(INPUT_CHANGED);
					if (e.getSource() == hiddenLayersSizeSpinner)
						parent.onSpinnersChange(HIDDEN_LAYERS_CHANGED);
					if (e.getSource() == outputVectorSizeSpinner)
						parent.onSpinnersChange(OUTPUT_CHANGED);
				} catch (NeuronNetException e1) {

					e1.printStackTrace();
				}
			}
		}
	}

	class LayersTabsPane extends JTabbedPane {

		JFileChooser fileChooser = new JFileChooser();
		JRadioButton manualInputButton = new JRadioButton("manual data input");
		JRadioButton fileInputButton = new JRadioButton("data from file");
		JButton readDataButton = new JButton("read data vector from file");
		SignalPanel signalPanel = new SignalPanel(new GridBagLayout());
		BiasLayerTab outputLayerTab = new BiasLayerTab(Layer.OUTPUT_LAYER, getOutputVectorSize());

		public LayersTabsPane() {
			addTab("Input layer", signalPanel);
			addTab("Output layer", outputLayerTab);
			Net.getInstance().setActivationFunctionForLayer(Layer.OUTPUT_LAYER,
					(ActivationFunc) outputLayerTab.functionChoser.getSelectedItem());
		}

		public void extendDataVector() {
			signalPanel.extendDataVector();
		}

		public void reduceDataVector() {
			signalPanel.reduceDataVector();
		}

		public void extendOutputNumber() {
			outputLayerTab.extendBiasInput();
		}

		public void reduceOutputNumber() {
			outputLayerTab.reduceBiasInput();
		}

		private class SignalPanel extends JPanel implements ActionListener {

			LinkedList<Component> components = new LinkedList<Component>();
			JPanel dataPanel = new JPanel(new GridLayout(0, 2));

			public SignalPanel(LayoutManager manager) {
				super(manager);

				readDataButton.addActionListener(this);
				readDataButton.setEnabled(false);

				ButtonGroup dataSourceButtonGroup = new ButtonGroup();
				manualInputButton.addActionListener(this);
				manualInputButton.setSelected(true);
				fileInputButton.addActionListener(this);
				dataSourceButtonGroup.add(manualInputButton);
				dataSourceButtonGroup.add(fileInputButton);

				JPanel fileInputPanel = new JPanel(new GridLayout(2, 1));
				fileInputPanel.add(fileInputButton);
				fileInputPanel.add(readDataButton);

				JPanel dataInputModePanel = new JPanel(new GridBagLayout());
				GridBagConstraints c = new GridBagConstraints();
				c.anchor = GridBagConstraints.NORTHWEST;

				dataInputModePanel.add(manualInputButton, c);
				dataInputModePanel.add(fileInputPanel, c);

				c.gridwidth = GridBagConstraints.REMAINDER;
				add(new EtchedBorderPanel(dataInputModePanel, "Data input mode"), c);

				dataVector = new LinkedList<JTextField>();
				extendDataVector();
				c.fill = GridBagConstraints.HORIZONTAL;
				add(new EtchedBorderPanel(dataPanel, "Data vector"), c);
			}

			public void extendDataVector() {
				dataVector.addLast(new JTextField(5));
				if (fileInputButton.isSelected())
					dataVector.getLast().setEnabled(false);

				JPanel inputSignalPanel = new JPanel(new GridLayout(1, 2));
				inputSignalPanel.add(new JLabel(dataVector.size() + " signal: "));
				inputSignalPanel.add(dataVector.getLast());
				dataPanel.add(inputSignalPanel);
				components.addLast(inputSignalPanel);
				updateUI();
			}

			public void reduceDataVector() {
				dataPanel.remove(components.removeLast());
				dataVector.removeLast();
				updateUI();
			}

			private void fillInputSignalPanelWithPassedSignalData(List<Double> inputVector) {
				int currentInputVectorSize = getInputVectorSize();
				int neededVectorSize = inputVector.size();
				int difference = neededVectorSize - currentInputVectorSize;

				for (int i = 0; i < difference; i++)
					extendDataVector();
				for (int i = 0; i > difference; i--)
					reduceDataVector();

				for (int i = 0; i < inputVector.size(); i++) {
					dataVector.get(i).setText(inputVector.get(i).toString());
				}

				overalNetStructurePanel.inputVectorSizeSpinner.setValue(neededVectorSize);
			}

			private List<List<Double>> readInputsFromFile() throws IOException {
				List<List<Double>> inputs = new LinkedList<List<Double>>();

				BufferedReader reader = new BufferedReader(new FileReader(fileChooser.getSelectedFile()));
				String line = null;
				while ((line = reader.readLine()) != null) {
					List<Double> inputVector = new LinkedList<Double>();
					String[] values = line.split("\\s+");
					for (String d : values) {
						inputVector.add(Double.parseDouble(d));
					}
					inputs.add(inputVector);
				}
				return inputs;
			}

			@Override
			public void actionPerformed(ActionEvent e) {
				if (e.getSource() == readDataButton) {
					int retVal = fileChooser.showDialog(this, "choose file");

					if (retVal == JFileChooser.APPROVE_OPTION) {
						try {
							// List<Double> inputVector = readFromFile();
							List<List<Double>> inputs = readInputsFromFile();
							Net.getInstance().setInputsValues(inputs.subList(0, inputs.size() / 2));
							Net.getInstance().setDesiredOutput(inputs.subList(inputs.size() / 2, inputs.size()));
							fillInputSignalPanelWithPassedSignalData(inputs.get(0));
						} catch (NumberFormatException e1) {
							JOptionPane.showMessageDialog(this, "Sorry, specifed file has wrong format.\n"
									+ "Each signal has to be a number in separate line", "NumberFormatException",
									JOptionPane.ERROR_MESSAGE);
						} catch (IOException e1) {
							JOptionPane.showMessageDialog(this, "Sorry, I was unable to process input file",
									"IOException", JOptionPane.ERROR_MESSAGE);
						}
					}
				}
				if (e.getSource() == manualInputButton) {
					readDataButton.setEnabled(false);
					for (JTextField signal : dataVector)
						signal.setEnabled(true);
				}
				if (e.getSource() == fileInputButton) {
					readDataButton.setEnabled(true);
					for (JTextField signal : dataVector)
						signal.setEnabled(false);
				}
			}
		}
	}

	class BiasLayerTab extends JPanel implements ActionListener {
		public final int layerNr;
		protected LinkedList<JTextField> biasVector;
		protected LinkedList<Component> components;
		protected JPanel mainPanel;
		protected JComboBox functionChoser;
		protected JTextField fromWeightTextField;
		protected JTextField toWeightTextField;
		protected JTextField learnigFactor;
		protected JTextField learnigSteps;

		public BiasLayerTab(int layerNr, int biasVectorSize) {
			this.layerNr = layerNr;
			init();
			for (int i = 0; i < biasVectorSize; i++) {
				extendBiasInput();
			}
		}

		private void init() {
			biasVector = new LinkedList<JTextField>();
			components = new LinkedList<Component>();
			mainPanel = new JPanel(new GridLayout(0, 1));
			functionChoser = new JComboBox(ActivationFunc.functions);
			learnigFactor = new JTextField("0.7");

			JPanel fromWeightPanel = new JPanel(new GridLayout());
			fromWeightPanel.add(new JLabel("Weight from"));
			fromWeightTextField = new JTextField("-0.1");
			fromWeightPanel.add(fromWeightTextField);

			JPanel toWeightPanel = new JPanel(new GridLayout());
			toWeightPanel.add(new JLabel("Weight to"));
			toWeightTextField = new JTextField("0.1");
			toWeightPanel.add(toWeightTextField);

			mainPanel.add(functionChoser);

			JPanel learnFactorPanel = new JPanel(new GridLayout());
			learnFactorPanel.add(new JLabel("Learn factor"));
			learnFactorPanel.add(learnigFactor);
			mainPanel.add(learnFactorPanel);

			JPanel learnStepPanel = new JPanel(new GridLayout());
			learnStepPanel.add(new JLabel("Steps"));
			learnigSteps = new JTextField("100");
			learnStepPanel.add(learnigSteps);
			mainPanel.add(learnStepPanel);

			mainPanel.add(fromWeightPanel);
			mainPanel.add(toWeightPanel);
			add(mainPanel);

			functionChoser.addActionListener(this);
			functionChoser.setActionCommand("activationFunc");
		}

		public void generateRandomWeights() {
			Net.getInstance().outputLayer.assignRandomWeights(Double.valueOf(fromWeightTextField.getText()),
					Double.valueOf(toWeightTextField.getText()));
		}

		public void stimulateLayer() {
			ArrayList<Double> values = new ArrayList<Double>();
			for (JTextField field : biasVector) {
				if (field.getText().isEmpty())
					values.add(0.1);
				else {
					values.add(Double.parseDouble(field.getText()));
				}
			}
			Net.getInstance().fillLayerWithBiases(layerNr, values);
		}

		protected void extendBiasInput() {
			JPanel inputPanel = new JPanel(new GridLayout(1, 2));
			JLabel biasLabel = new JLabel("Bias: " + (biasVector.size() + 1));
			JTextField biasField = new JTextField();
			inputPanel.add(biasLabel);
			inputPanel.add(biasField);
			biasVector.addLast(biasField);
			components.addLast(inputPanel);
			mainPanel.add(inputPanel);
			updateUI();
		}

		protected void reduceBiasInput() {
			mainPanel.remove(components.removeLast());
			biasVector.removeLast();
			updateUI();
		}

		public void setBiasVector(String[] values) {
			for (int i = 0; i < values.length; i++) {
				biasVector.get(i).setText(values[i]);
			}
			updateUI();
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			if (e.getActionCommand().equals("activationFunc")) {
				Net.getInstance().setActivationFunctionForLayer(layerNr,
						(ActivationFunc) ((JComboBox) e.getSource()).getSelectedItem());
				try {
					NeuralGraph.getInstance(false).repaint();
				} catch (NeuronNetException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			}
		}
	}

	class LayerTab extends BiasLayerTab implements ChangeListener {

		private JSpinner neuronsQuantitySpinner = new JSpinner(new SpinnerNumberModel(1, 1, Integer.MAX_VALUE, 1));

		public LayerTab(int layerNr) {
			super(layerNr, 1);
			init();

			try {
				int n = JOptionPane.showConfirmDialog(this, "Do you like to create Kohonen Layer ?", "Kohonen",
						JOptionPane.YES_NO_OPTION);
				if (n == JOptionPane.YES_OPTION) {
					Neuron neuron = Net.getInstance().addLayer();
					NeuralGraph.getInstance(false).addVertex(neuron, layerNr, false);
				} else {
					try {
						Neuron neuron = Net.getInstance().addKohonenLayer();
						learningComponent.setLearningParams();
						NeuralGraph.getInstance(false).addVertex(neuron, layerNr, false);
					} catch (NumberFormatException nfe) {
						JOptionPane.showMessageDialog(this, "Wrong input value", "Input Error",
								JOptionPane.ERROR_MESSAGE);
						nfe.printStackTrace();
					}

				}
			} catch (NeuronNetException ex) {
				ex.printStackTrace();
			}

			Net.getInstance().setActivationFunctionForLayer(layerNr, (ActivationFunc) functionChoser.getSelectedItem());
		}

		private void init() {
			int components = mainPanel.getComponents().length;
			mainPanel.add(new JLabel("Neurons quantity: "), components - 2);
			mainPanel.add(neuronsQuantitySpinner, components - 1);
			neuronsQuantitySpinner.addChangeListener(this);
		}

		public void generateRandomWeights() {
			Layer l = Net.getInstance().layersMap.get(layerNr);
			l.assignRandomWeights(Double.valueOf(fromWeightTextField.getText()),
					Double.valueOf(toWeightTextField.getText()));
		}

		public LayerTab(int layerNr, int neuronsQuantitySpinnerValue, boolean fromFile) {
			super(layerNr, neuronsQuantitySpinnerValue);
			init();
			neuronsQuantitySpinner.setValue(neuronsQuantitySpinnerValue);
			if (!fromFile)
				Net.getInstance().setActivationFunctionForLayer(layerNr, (ActivationFunc) functionChoser.getSelectedItem());
		}

		public LayerTab(int layerNr, int neuronsQuantitySpinnerValue, List<Double> bias, boolean fromFile) {
			this(layerNr, neuronsQuantitySpinnerValue, fromFile);
			for (int i = 0; i < bias.size(); i++) {
				biasVector.get(i).setText(bias.get(i).toString());
			}
		}

		@Override
		public void stateChanged(ChangeEvent event) {
			Net net = Net.getInstance();
			Integer currentNeuronsQuantity = (Integer) neuronsQuantitySpinner.getValue();
			int prevNeuronsQuantity = net.sizeOfLayer(layerNr);
			int difference = currentNeuronsQuantity - prevNeuronsQuantity;
			for (int i = 0; i < difference; i++) {
				try {
					Neuron neuron = net.addNeuron(layerNr);
					NeuralGraph.getInstance(false).addVertex(neuron, layerNr, false);
					extendBiasInput();
				} catch (NeuronNetException ex) {

					ex.printStackTrace();
				}
			}
			for (int i = difference; i < 0; i++) {
				try {
					Neuron neuron = net.removeLastNeuronInLayer(layerNr);
					NeuralGraph.getInstance(false).removeVertex(neuron);
					reduceBiasInput();
				} catch (NeuronNetException ex) {

					ex.printStackTrace();
				}
			}
		}
	}
}
